<!DOCTYPE html> <!-- -*- html -*- -->
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>2x2 Matrix Transformations</title>
  <link rel="shortcut icon" href="img/gatech.gif"/>

  
      <link rel="stylesheet" href="css/demo.css?vers=2759ff">
  

  <style>
      
#help-text {
    text-align: center;
}

  </style>

</head>
<body>
    

    
        <script src="js/demo.js?vers=77646a"></script>
    

    <script type="text/javascript">
        "use strict";
        DomReady.ready(function() {

        var color1, color2, ortho, ref,
  bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

ortho = 10000;

color1 = new Color("green");

color2 = new Color("red");

window.demo = new Demo2D({
  preload: {
    image: 'img/' + ((ref = urlParams.pic) != null ? ref : "theo2.jpg")
  },
  ortho: ortho,
  camera: {
    position: [1.1, 0, ortho],
    lookAt: [1.1, 0, 0]
  },
  vertical: 2.2
}, function() {
  var Params, axisOpts, clipCube, computeIn, computeMatrix, computeOut, drag2, eqnElt, folder, gridOpts, gui, hiliteOpts, i, inVec, inverse, j, labelOpts, matrix, numTransforms, outVec, params, paramses, ref1, updateCaption, vectorOpts, view1, view2, zeroOpts;
  window.mathbox = this.mathbox;
  matrix = this.urlParams.get('mat', 'float[]', [1, 0, 0, 1]);
  numTransforms = this.urlParams.get('num', 'int', 3);
  inverse = null;
  updateCaption = (function(_this) {
    return function() {
      var str;
      str = _this.texMatrix([[matrix[0], matrix[2]], [matrix[1], matrix[3]]]);
      str += _this.texVector(inVec, {
        color: color1
      });
      str += '=' + _this.texVector(outVec, {
        color: color2
      });
      return katex.render(str, eqnElt);
    };
  })(this);
  computeMatrix = (function(_this) {
    return function(first) {
      var a, b, c, d, det, j, len, mat, mult, params;
      if (!first) {
        mat = [1, 0, 0, 1];
        for (j = 0, len = paramses.length; j < len; j++) {
          params = paramses[j];
          mult = params.matrix;
          a = mult[0] * mat[0] + mult[1] * mat[2];
          b = mult[0] * mat[1] + mult[1] * mat[3];
          c = mult[2] * mat[0] + mult[3] * mat[2];
          d = mult[2] * mat[1] + mult[3] * mat[3];
          mat = [a, b, c, d];
        }
        matrix[0] = mat[0];
        matrix[1] = mat[1];
        matrix[2] = mat[2];
        matrix[3] = mat[3];
      }
      det = matrix[0] * matrix[3] - matrix[1] * matrix[2];
      if (det * det < 0.00001) {
        inverse = null;
        drag2.enabled = false;
      } else {
        inverse = [matrix[3] / det, -matrix[1] / det, -matrix[2] / det, matrix[0] / det];
        drag2.enabled = true;
      }
      computeOut();
      return updateCaption();
    };
  })(this);
  Params = (function() {
    function Params() {
      this.doRotate = bind(this.doRotate, this);
      this.doYShear = bind(this.doYShear, this);
      this.doXShear = bind(this.doXShear, this);
      this.doScale = bind(this.doScale, this);
      this.xscale = 1.0;
      this.yscale = 1.0;
      this.xshear = 0.0;
      this.yshear = 0.0;
      this.rotate = 0.0;
      this.matrix = [1, 0, 0, 1];
    }

    Params.prototype.doScale = function() {
      this.rotate = this.xshear = this.yshear = 0.0;
      this.matrix = [this.xscale, 0, 0, this.yscale];
      return computeMatrix();
    };

    Params.prototype.doXShear = function() {
      this.xscale = this.yscale = 1.0;
      this.rotate = this.yshear = 0.0;
      this.matrix = [1, this.xshear, 0, 1];
      return computeMatrix();
    };

    Params.prototype.doYShear = function() {
      this.xscale = this.yscale = 1.0;
      this.rotate = this.xshear = 0.0;
      this.matrix = [1, 0, this.yshear, 1];
      return computeMatrix();
    };

    Params.prototype.doRotate = function() {
      var c, s;
      this.xscale = this.yscale = 1.0;
      this.xshear = this.yshear = 0.0;
      c = Math.cos(this.rotate);
      s = Math.sin(this.rotate);
      this.matrix = [c, -s, s, c];
      return computeMatrix();
    };

    return Params;

  })();
  paramses = [];
  gui = new dat.GUI();
  gui.closed = this.urlParams.closed != null;
  for (i = j = 0, ref1 = numTransforms; 0 <= ref1 ? j < ref1 : j > ref1; i = 0 <= ref1 ? ++j : --j) {
    folder = gui.addFolder("Transform " + (i + 1));
    params = new Params();
    if (i === 0) {
      folder.open();
    }
    folder.add(params, 'xscale', -2, 2).step(0.05).onChange(params.doScale).listen();
    folder.add(params, 'yscale', -2, 2).step(0.05).onChange(params.doScale).listen();
    folder.add(params, 'rotate', -π, π).step(0.1 * π).onChange(params.doRotate).listen();
    folder.add(params, 'xshear', -2, 2).step(0.05).onChange(params.doXShear).listen();
    folder.add(params, 'yshear', -2, 2).step(0.05).onChange(params.doYShear).listen();
    paramses.push(params);
  }
  gridOpts = {
    color: 'white',
    opacity: 0.25,
    width: 1,
    zOrder: 1,
    zIndex: 1
  };
  axisOpts = {
    color: 'white',
    opacity: 0.6,
    zIndex: 1,
    zOrder: 1,
    size: 3
  };
  view1 = this.view({
    name: 'view1',
    gridOpts: gridOpts,
    axisOpts: axisOpts,
    axisLabels: false
  });
  view1.image({
    image: this.image
  }).matrix({
    width: 2,
    height: 2,
    channels: 2,
    data: [[[-10, -10], [10, -10]], [[-10, 10], [10, 10]]]
  }).surface({
    color: 'white',
    points: '<',
    map: '<<',
    fill: true,
    zOrder: 0
  });
  view2 = this.view({
    name: 'view2',
    viewOpts: {
      position: [2.2, 0, 0]
    },
    gridOpts: gridOpts,
    axisOpts: axisOpts,
    axisLabels: false
  });
  clipCube = this.clipCube(view2, {
    draw: false,
    hilite: false,
    range: 10.0,
    pass: 'view'
  });
  clipCube.clipped.transform({}, {
    matrix: function() {
      return [matrix[0], matrix[1], 0, 0, matrix[2], matrix[3], 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    }
  }).image({
    image: this.image
  }).matrix({
    width: 2,
    height: 2,
    channels: 2,
    data: [[[-10, -10], [10, -10]], [[-10, 10], [10, 10]]]
  }).surface({
    color: 'white',
    points: '<',
    map: '<<',
    fill: true,
    zOrder: 0
  });
  computeOut = function() {
    outVec[0] = matrix[0] * inVec[0] + matrix[1] * inVec[1];
    return outVec[1] = matrix[2] * inVec[0] + matrix[3] * inVec[1];
  };
  computeIn = function() {
    if (inverse != null) {
      inVec[0] = inverse[0] * outVec[0] + inverse[1] * outVec[1];
      return inVec[1] = inverse[2] * outVec[0] + inverse[3] * outVec[1];
    }
  };
  vectorOpts = {
    size: 4,
    width: 3,
    zIndex: 2
  };
  zeroOpts = {
    zIndex: 2,
    size: 15
  };
  labelOpts = {
    offset: [0, 25],
    size: 15,
    zIndex: 3,
    outline: 0,
    background: "white"
  };
  hiliteOpts = {
    zTest: true,
    zWrite: true,
    zOrder: 2,
    opacity: 0.5
  };
  inVec = [2, 4, 0];
  this.labeledVectors(view1, {
    name: 'labeled1',
    vectors: [inVec],
    colors: [color1.brighten(.1)],
    labels: ['x'],
    live: true,
    zeroPoints: true,
    zeroThreshold: 0.3,
    vectorOpts: vectorOpts,
    labelOpts: labelOpts,
    zeroOpts: zeroOpts
  });
  this.draggable(view1, {
    name: 'drag1',
    points: [inVec],
    onDrag: computeOut,
    postDrag: updateCaption,
    hiliteOpts: hiliteOpts
  });
  outVec = [2, 4, 0];
  this.labeledVectors(view2, {
    name: 'labeled2',
    vectors: [outVec],
    colors: [color2.brighten(.1)],
    labels: ['b'],
    live: true,
    zeroPoints: true,
    zeroThreshold: 0.3,
    vectorOpts: vectorOpts,
    labelOpts: labelOpts,
    zeroOpts: zeroOpts
  });
  drag2 = this.draggable(view2, {
    name: 'drag2',
    points: [outVec],
    onDrag: computeIn,
    postDrag: updateCaption,
    hiliteOpts: hiliteOpts
  });
  this.caption('<p><span id="eqn-here"></span></p>\n<p id="help-text">[Click and drag the vector heads]</p>');
  eqnElt = document.getElementById('eqn-here');
  return computeMatrix(true);
});


        });
    </script>
</body>
</html>

